import * as THREE from "three"

import r from "../math/69626"
import lineEnum from "../enum/line.enum"
import Line from "../mesh/Line"
import LineEndpoint from "../mesh/LineEndpoint"
import renderOrderEnum from "../enum/renderOrder.enum"
import LineGeometry from "./LineGeometry"
import { LineEndpointMaterial, LineMaterial } from "../module/LinesModule"

export default class LineSegment {
  lineMaterial: LineMaterial
  options: any
  endpointMaterial: LineEndpointMaterial
  startPosition: THREE.Vector3
  endPosition: THREE.Vector3
  endpoints: boolean
  isVisible: boolean
  meshToLinePartMap: Record<string, LineEndpoint | Line>
  lineMesh: Line
  endMesh: LineEndpoint
  startMesh: LineEndpoint
  lastBillboardOptions: any
  updateSelected: (e: any) => this
  setRenderLayer: (e: any) => void
  setRenderOrder: (e: any) => void
  hide: () => void
  toggle: (e: any) => this
  dispose: () => void
  overrideLineMaterial: (e: any) => void
  restoreLineMaterial: () => void
  updateResolution: any
  show: any
  opacity: any
  updatePositions: any
  constructor(e: THREE.Vector3, t: THREE.Vector3, i: LineMaterial, o: any, l?) {
    this.lineMaterial = i
    this.options = o
    this.endpointMaterial = l
    this.startPosition = new THREE.Vector3()
    this.endPosition = new THREE.Vector3()
    this.endpoints = !1
    this.isVisible = !0
    this.meshToLinePartMap = {}
    this.opacity = e => (this.endpointMaterial && this.endpointMaterial.setOpacity(e), this.lineMesh.material.setOpacity(e), this)
    this.updatePositions = (e, t) => (
      this.startPosition.copy(e),
      this.endPosition.copy(t),
      this.options.beforeUpdatePositions && ((e = this.options.beforeUpdatePositions(e)), (t = this.options.beforeUpdatePositions(t))),
      this.lineMesh.geometry.setPositions([e.x, e.y, e.z, t.x, t.y, t.z]),
      this.endpoints &&
        (this.endMesh.position.copy(t), this.startMesh.position.copy(e), this.lastBillboardOptions && this.updateBillboard(this.lastBillboardOptions)),
      this.lineMesh.material.dashed && this.lineMesh.computeLineDistances(),
      this
    )
    this.updateBillboard = e => {
      if (
        this.endpoints &&
        (e.rotation && (this.endMesh.quaternion.copy(e.rotation), this.startMesh.quaternion.copy(e.rotation)),
        e.position && e.projection && !r.s1(e.projection))
      ) {
        const t = this.lineMesh.material.resolution.y
        this.endMesh.billboardScale(e.position, e.projection, t), this.startMesh.billboardScale(e.position, e.projection, t)
      }
      this.lastBillboardOptions = e
      return this
    }
    this.updateSelected = e => (
      this.lineMesh.material.updateSelected(e),
      this.endpoints && (this.endMesh.material.updateHovered(e), this.endMesh.updateSelected(e), this.startMesh.updateSelected(e)),
      this
    )
    this.setRenderLayer = e => {
      this.children.forEach(t => (t.layers.mask = e.mask))
    }
    this.setRenderOrder = e => {
      this.children.forEach(t => (t.renderOrder = e))
    }
    this.updateResolution = (e, t) => (this.lineMesh.material.resolution.set(e, t), this)
    this.hide = () => {
      this.options.onHide && this.options.onHide(), (this.isVisible = !1)
    }
    this.show = () => {
      this.options.onShow && this.options.onShow(), (this.isVisible = !0)
    }
    this.toggle = e => (e ? this.show() : this.hide(), this)
    this.dispose = () => {
      this.options.onHide && this.options.onHide()
      this.lineMesh.geometry.dispose()
      this.lineMesh.material.dispose()
      this.endMesh && this.endMesh.dispose()
      this.startMesh && this.startMesh.dispose()
      this.meshToLinePartMap = {}
    }
    this.overrideLineMaterial = e => {
      this.lineMesh.material = e
    }
    this.restoreLineMaterial = () => {
      this.lineMesh.material = this.lineMaterial
    }
    this.startPosition.copy(e)
    this.endPosition.copy(t)
    this.lineMesh = new Line(new LineGeometry(), i)
    this.lineMesh.matrixAutoUpdate = !1
    this.lineMesh.part = lineEnum.line
    this.lineMesh.renderOrder = renderOrderEnum.lines
    this.meshToLinePartMap[lineEnum.line] = this.lineMesh
    l &&
      ((this.endpoints = !0),
      (this.endMesh = new LineEndpoint(new THREE.PlaneBufferGeometry(1, 1), l)),
      (this.endMesh.part = lineEnum.end),
      (this.endMesh.renderOrder = renderOrderEnum.endpoints),
      (this.meshToLinePartMap[lineEnum.end] = this.endMesh),
      (this.startMesh = new LineEndpoint(new THREE.PlaneBufferGeometry(1, 1), l)),
      (this.startMesh.part = lineEnum.start),
      (this.startMesh.renderOrder = renderOrderEnum.endpoints),
      (this.meshToLinePartMap[lineEnum.start] = this.startMesh))
    this.updatePositions(this.startPosition, this.endPosition)
    this.updateSelected(!1)
  }
  updateBillboard: (e?: any) => LineSegment
  get currentOpacity() {
    return this.lineMesh.material.getOpacity()
  }
  getMesh(e) {
    return this.meshToLinePartMap[e]
  }
  get children() {
    return Object.keys(this.meshToLinePartMap).map(e => this.meshToLinePartMap[e])
  }
  get visible() {
    return this.isVisible
  }
}
